/**
 * Copyright 2016-2018 mobaas.com
 */
package com.mobaas.devops.node.schedule;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mobaas.devops.model.ContainerStat;
import com.mobaas.devops.node.HttpResult;
import com.mobaas.devops.node.HttpUtil;
import com.mobaas.devops.node.docker.DockerCertificateException;
import com.mobaas.devops.node.docker.DockerClient;

/**
 * 
 * @author billy zhang
 *
 */
@Component
@Configurable
@EnableScheduling
public class ContainerStatScheduler {

	@Autowired
	private ObjectMapper jsonMapper;
	
	private DockerClient dockerClient;
	
	@Value("${docker.host}")
	private String host;
	
	@Value("${docker.cert-path}")
	private String certPath;

	@Value("${api.url}")
	private String apiUrl;
	
	@Scheduled(cron="${docker.containerstat.cron}")
	public void gatheContainerStat() {
		
		try {
			dockerClient = new DockerClient(host, certPath);
		} catch (DockerCertificateException e1) {
			e1.printStackTrace();
			return;
		}
		
		// 获取运行中的容器
		HttpResult result = dockerClient.listContainers();
		if (result.isSuccess()) {
			HashMap[] maps = null;
			
			try {
				maps = jsonMapper.readValue(result.getContent(), HashMap[].class);
			} catch (IOException e) {
				e.printStackTrace();
			}
			
			if (maps == null)
				return;
			
			List<ContainerStat> statlist = new ArrayList<>();
			
			for (int n=0; n<maps.length; n++) {
				try {
					String containerId = ((String)maps[n].get("Id"));
					String name = "";
					int publicPort = 0;
					
					String[] names = (String[])maps[n].get("Names");
					if (names != null) {
						name = names[0];
					}
					
					HashMap[] portsMap = (HashMap[])maps[n].get("Ports");
					if (portsMap != null) {
						publicPort = (Integer)portsMap[0].get("PublicPort");
					}
					
					result = dockerClient.getContainerStats(containerId);
					if (result.isSuccess()) {
						HashMap map = jsonMapper.readValue(result.getContent(), HashMap.class);
						
						ContainerStat stat = new ContainerStat();
						stat.setContainerId(containerId);
						stat.setAppId(name);
						stat.setHostIp(host);
						stat.setPublicPort(publicPort);
						stat.setAddTime(new Date());
						
						// pids
						stat.setPids( Short.parseShort(((HashMap)map.get("pids_stats")).get("current").toString()) );
						
						// cpu
						HashMap cpu_stats = (HashMap)map.get("cpu_stats");
						long total_usage = Long.parseLong( ((HashMap)cpu_stats.get("cpu_usage")).get("total_usage").toString() );
						long system_cpu_usage = Long.parseLong( cpu_stats.get("system_cpu_usage").toString() );
						
						HashMap precpu_stats = (HashMap)map.get("precpu_stats");
						long pre_total_usage = Long.parseLong( ((HashMap)precpu_stats.get("cpu_usage")).get("total_usage").toString() );
						long pre_system_cpu_usage = Long.parseLong( precpu_stats.get("system_cpu_usage").toString() );
						long pre_online_cpus = Long.parseLong( precpu_stats.get("online_cpus").toString() );
						
						stat.setCpu( ((total_usage - pre_total_usage) * 1.0f / (system_cpu_usage - pre_system_cpu_usage)) * 100 ); 
						
						// memory
						HashMap memory_stats = (HashMap)map.get("memory_stats");
						stat.setMemUsage( Long.parseLong((memory_stats.get("usage").toString()).toString()) );
						stat.setMemMaxUsage( Long.parseLong((memory_stats.get("max_usage").toString()).toString()) );
						stat.setMemLimit( Long.parseLong((memory_stats.get("limit").toString()).toString()) );
						
						// network
						long rx_total_bytes = 0;
						long tx_total_bytes = 0;
						HashMap networks = (HashMap)map.get("networks");
						for (Object key : networks.keySet()) {
							HashMap eth = (HashMap)networks.get(key);
							rx_total_bytes += Long.parseLong( eth.get("rx_bytes").toString() );
							tx_total_bytes += Long.parseLong( eth.get("tx_bytes").toString() );
						}
						
						stat.setNetIn(rx_total_bytes);
						stat.setNetOut(tx_total_bytes);
						
						// block
						long blkio_read = 0;
						long blkio_write = 0;
						HashMap blkio_stats = (HashMap)map.get("blkio_stats");
						if (blkio_stats.containsKey("io_service_bytes_recursive")) {
							ArrayList list = (ArrayList)blkio_stats.get("io_service_bytes_recursive");
							for (int i=0; i<list.size(); i++) {
								HashMap val = (HashMap)list.get(i);
								if ("Read".equals((String)val.get("op"))) {
									blkio_read += Long.parseLong( val.get("value").toString() );
								} else if ("Write".equals((String)val.get("op"))) {
									blkio_write += Long.parseLong( val.get("value").toString() );
								}
							}
						}
						if (blkio_stats.containsKey("io_serviced_recursive")) {
							ArrayList list = (ArrayList)blkio_stats.get("io_serviced_recursive");
							for (int i=0; i<list.size(); i++) {
								HashMap val = (HashMap)list.get(i);
								if ("Read".equals((String)val.get("op"))) {
									blkio_read += Long.parseLong( val.get("value").toString() );
								} else if ("Write".equals((String)val.get("op"))) {
									blkio_write += Long.parseLong( val.get("value").toString() );
								}
							}
						}
						
						stat.setBlockIn(blkio_read);
						stat.setBlockOut(blkio_write);
						
						statlist.add(stat);
					}
					
				} catch (Exception ex) {
					ex.printStackTrace();
				}
			}
			
			if (statlist.size() > 0) {
				try {
					String json = jsonMapper.writeValueAsString(statlist);
					HttpResult r = HttpUtil.post(apiUrl, json);
				} catch (JsonProcessingException e) {
					e.printStackTrace();
				}
			}
		}
		
	}
}
